package com.lidong.zc.databasemodel.utils;

import com.lidong.zc.databasemodel.common.InitDataBaseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ObjectUtils;
import org.springframework.util.SystemPropertyUtils;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * @author LiDong
 * @version 1.0.0
 * @createTime 12/10/2022 6:54 PM
 */
public class LowCodeDataBaseResourceLoader implements ResourceLoaderAware {

    private static final Logger logger = LoggerFactory.getLogger(LowCodeDataBaseResourceLoader.class);
    public static final String PATTERN_CLASS = "/**/*.class";

    /**
     * 需要扫描的
     */
    private final List<TypeFilter> includeTypeFilterList = new LinkedList<>();
    /**
     * 不需要扫描的
     */
    private final List<TypeFilter> excludeTypeFilterList = new LinkedList<>();

    private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
    }

    /**
     * 获取包下面注解标注的类
     *
     * @param basePackages 包路径 com.lidong.lowcode
     * @param annotations  需要扫描的注解
     * @return List
     */
    public static <T> List<Class<T>> getAllClassByAnnotation(String[] basePackages, Class<? extends Annotation>... annotations) {
        LowCodeDataBaseResourceLoader lowCodeDataBaseResourceLoader = new LowCodeDataBaseResourceLoader();
        if (!ObjectUtils.isEmpty(annotations)) {
            for (Class<? extends Annotation> anooationClass : annotations) {
                lowCodeDataBaseResourceLoader.addIncludeFilter(new AnnotationTypeFilter(anooationClass));
            }
        }
        List<Class<T>> tableClassList = new ArrayList<>();
        for (String pack : basePackages) {
            tableClassList.addAll(lowCodeDataBaseResourceLoader.scanPackageGetClassList(pack));
        }
        return tableClassList;
    }

    /**
     * 扫描包获取类
     *
     * @param basePackage 包路径
     * @param <T>         T
     * @return List
     */
    public <T> List<Class<T>> scanPackageGetClassList(String basePackage) {
        List<Class<T>> classes = new ArrayList<>();
        try {
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + org.springframework.util.ClassUtils
                    .convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage)) + PATTERN_CLASS;
            Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
            for (Resource resource : resources) {
                if (resource.isReadable()) {
                    MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
                    if (matchTypeFilter(metadataReader)) {
                        try {
                            ClassMetadata classMetadata = metadataReader.getClassMetadata();
                            Class<?> aClass = Class.forName(classMetadata.getClassName());
                            classes.add((Class<T>) aClass);
                        } catch (Exception e) {
                            logger.error(e.getMessage(), e);
                        }
                    }
                }
            }
        } catch (Exception e) {
            throw new InitDataBaseException(e.getMessage(), e);
        }
        return classes;
    }

    /**
     * 添加需要扫描的规则
     *
     * @param includeFilter includeFilter
     */
    private void addIncludeFilter(TypeFilter includeFilter) {
        includeTypeFilterList.add(includeFilter);
    }

    /**
     * 匹配过滤规则
     *
     * @param metadataReader metadataReader
     * @return boolean
     * @throws IOException exception
     */
    private boolean matchTypeFilter(MetadataReader metadataReader) throws IOException {
        for (TypeFilter typeFilter : excludeTypeFilterList) {
            if (typeFilter.match(metadataReader, metadataReaderFactory)) {
                return false;
            }
        }
        for (TypeFilter typeFilter : includeTypeFilterList) {
            if (typeFilter.match(metadataReader, metadataReaderFactory)) {
                return true;
            }
        }
        return false;
    }

}
